#include "GlWindow.h"

#define WindowClassName "GlWindow"

namespace GraphicLibrary
{
	const char	Window::msDefaultTitle[] = "GlWindow";
	//------------------------------------
	void Window::init(const UINT width, const UINT height, const int top, const int left, const char *title, const HWND pHwnd)
	{
		mExStyle = WS_EX_OVERLAPPEDWINDOW;
		mStyle = WS_OVERLAPPEDWINDOW & (~WS_THICKFRAME);
		mMenu = 0;

		RECT rect;
		mRect.top = rect.top = top;
		mRect.bottom = rect.bottom = top + height;
		mRect.left = rect.left = left;
		mRect.right = rect.right = left + width;
		AdjustWindowRectEx(&rect, mStyle, mMenu != 0, mExStyle);

		mHwnd = CreateWindowEx(mExStyle, WindowClassName, title, mStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pHwnd, mMenu, GetModuleHandle(0), 0);

		if(!mHwnd)
			throw;

		SetWindowLong(mHwnd, GWL_USERDATA, (LONG)this);
		ShowWindow(mHwnd, SW_SHOW);
	}

	//------------------------------------
	LRESULT CALLBACK Window::WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		Window *window = (Window*)GetWindowLong(hWnd, GWL_USERDATA);
		switch(message)
		{
			case WM_CLOSE :
				{
					Caller::CallType c = (Caller::CallType)&WindowListener::afterWindowClosed;
					window->distribute(Caller(c));
					break;
				}
			case WM_MOVE :
				{
					WORD x = LOWORD(lParam);
					WORD y = HIWORD(lParam);

					LONG width = window->mRect.right - window->mRect.left;
					LONG height = window->mRect.bottom - window->mRect.top;

					window->mRect.left = x;
					window->mRect.top = y;

					window->mRect.bottom = window->mRect.top + height;
					window->mRect.right = window->mRect.left + width;
					//window->onMove(window);
					break;
				}
			case WM_SIZE :
				{
					WORD width = LOWORD(lParam);
					WORD height = HIWORD(lParam);
					window->mRect.right = window->mRect.left + width;
					window->mRect.bottom = window->mRect.top + height;
					//window->onResize(window);
					break;
				}
			case WM_MOUSEMOVE :
				{
					float x = LOWORD(lParam) / static_cast<float>(window->getClientAreaWidth()); 
					float y = HIWORD(lParam) / static_cast<float>(window->getClientAreaHeight());
					typedef Caller2<float, float> Call;
					Call::CallType c = (Call::CallType)&WindowListener::afterMouseMove;
					window->distribute(Call(c, x, y));
					break;
				}
			case WM_LBUTTONDOWN :
				{
					float x = LOWORD(lParam) / static_cast<float>(window->getClientAreaWidth()); 
					float y = HIWORD(lParam) / static_cast<float>(window->getClientAreaHeight());
					typedef Caller3<WindowListener::ButtonId, float, float> Call;
					Call::CallType c = (Call::CallType)&WindowListener::afterMouseDown;
					window->distribute(Call(c, WindowListener::BiLeft, x, y));
					break;
				}
			case WM_LBUTTONUP :
				{
					float x = LOWORD(lParam) / static_cast<float>(window->getClientAreaWidth()); 
					float y = HIWORD(lParam) / static_cast<float>(window->getClientAreaHeight());
					typedef Caller3<WindowListener::ButtonId, float, float> Call;
					Call::CallType c = (Call::CallType)&WindowListener::afterMouseUp;
					window->distribute(Call(c, WindowListener::BiLeft, x, y));
					break;
				}
			case WM_RBUTTONDOWN :
				{
					float x = LOWORD(lParam) / static_cast<float>(window->getClientAreaWidth()); 
					float y = HIWORD(lParam) / static_cast<float>(window->getClientAreaHeight());
					typedef Caller3<WindowListener::ButtonId, float, float> Call;
					Call::CallType c = (Call::CallType)&WindowListener::afterMouseDown;
					window->distribute(Call(c, WindowListener::BiRight, x, y));
					break;
				}
			case WM_RBUTTONUP :
				{
					float x = LOWORD(lParam) / static_cast<float>(window->getClientAreaWidth()); 
					float y = HIWORD(lParam) / static_cast<float>(window->getClientAreaHeight());
					typedef Caller3<WindowListener::ButtonId, float, float> Call;
					Call::CallType c = (Call::CallType)&WindowListener::afterMouseUp;
					window->distribute(Call(c, WindowListener::BiRight, x, y));
					break;
				}
			case WM_KEYDOWN :
				{
					unsigned int key = wParam;
					typedef Caller1<unsigned int> Call;
					Call::CallType c = (Call::CallType)&WindowListener::afterKeyDown;
					window->distribute(Call(c, key));
					break;
				}
			case WM_KEYUP :
				{
					unsigned int key = wParam;
					typedef Caller1<unsigned int> Call;
					Call::CallType c = (Call::CallType)&WindowListener::afterKeyUp;
					window->distribute(Call(c, key));
					break;
				}
		}
		return DefWindowProc(hWnd, message, wParam, lParam);
	}

	//------------------------------------
	Window::~Window()
	{
		if(mHwnd)
			DestroyWindow(mHwnd);
	}

	//------------------------------------
	void Window::manage()
	{
		MSG msg;
		while(PeekMessage(&msg, mHwnd, 0, 0, PM_REMOVE))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	//------------------------------------
	bool Window::Init()
	{
		WNDCLASSEX wndClass;
		ZeroMemory(&wndClass, sizeof(WNDCLASS));
		wndClass.cbSize = sizeof(WNDCLASSEX);
		wndClass.style = CS_HREDRAW | CS_VREDRAW;
		wndClass.lpfnWndProc = WndProc;
		wndClass.cbClsExtra = 0;
		wndClass.cbWndExtra = 0;
		wndClass.hInstance = GetModuleHandle(0);
		wndClass.hbrBackground = 0;
		wndClass.lpszMenuName = 0;
		wndClass.lpszClassName = WindowClassName;

		// ToDo:
		wndClass.hIcon = 0;
		wndClass.hCursor = 0;
		wndClass.hIconSm = 0;

#		pragma warning(disable: 4800)
		return RegisterClassEx(&wndClass);
#		pragma warning(default: 4800)
	}
}
